/**
 * @function defaultEquals 判断两个值是否相等
 */
export const defaultEquals = (a, b) => {
  return a === b;
}

/**
 * @class Node 添加到链表中的项
 * 
 * element 项的内容
 * next 指针指向下一个项
 */
export class Node {
  constructor(element){
    this._element = element;
    this._next = undefined;
  }
}


/**
 * @class LinkedList 单向链表
 * 
 * push(element)：向链表尾部添加一个新元素
 * insert(element, position)：向链表的特定位置插入一个新元素。
 * getElementAt(index)：返回链表中特定位置的元素。如果链表中不存在这样的元素，则返回 undefined。
 * remove(element)：从链表中移除一个元素。
 * indexOf(element)：返回元素在链表中的索引。如果链表中没有该元素则返回-1。
 * removeAt(position)：从链表的特定位置移除一个元素。
 * isEmpty()：如果链表中不包含任何元素，返回 true，如果链表长度大于 0则返回 false。
 * size()：返回链表包含的元素个数，与数组的 length 属性类似。
 * toString()：返回表示整个链表的字符串。由于列表项使用了 Node 类，就需要重写继承自 JavaScript 对象默认的 toString 方法，让其只输出元素的值。
 * 
 */
export class LinkedList {
  constructor(equalsFn = defaultEquals){
    this._count = 0; // 记录链表长度
    this._head = undefined;  // 记录头部元素
    this._equalsFn = equalsFn; // 用于判断项的内容是否相同
  }

  push(element){
    const node = new Node(element);
    let current;

    if(this._head == null){
      this._head = node;
    } else {
      current = this._head;
      // 寻找最后一个元素
      while(current._next !=  null){
        current = current._next;
      }
      current._next = node;
    }

    this._count++;
  }

  getElementAt(index){
    if(index >= 0 && index < this._count){
      let current = this._head;
      for (let i = 0; i < index && current != null; i++) {
        current = current._next; 
      }
      return current;
    }

    return undefined
  }

  removeAt(index){
    if(index >= 0 && index < this._count){
      let current = this._head;

      if(index == 0){
        this._head = current._next;
      } else {
        const previous = this.getElementAt(index -1);
        current = previous._next;
        // 将 previous 与 current 的下一项链接起来：跳过 current，从而移除它
        previous._next = current._next;
      }

      this._count--;
      return current._element;
    }

    return undefined;
  }

  insert(element, index){
    if(index >= 0 && index < this._count){
      const node = new Node(element);

      if(index == 0){
        const current = this._head;
        node._next = current;
        this._head = node;
      }else {
        const previous = this.getElementAt(index -1);
        const current = previous._next;
        node._next = current;
        previous._next = node;
      }

      this._count ++;
      return true;
    }

    return false;
  }

  indexOf(element){
    let current = this._head;
    for(let i = 0; i < this._count && current != null; i++){
      if(this._equalsFn(element, current._element)){
        return i;
      }
      current = current._next;
    }

    return -1;
  }

  remove(element) { 
    const index = this.indexOf(element); 
    return this.removeAt(index); 
  }

  size() { 
    return this._count; 
  }

  isEmpty() { 
    return this.count === 0; 
  }

  getHead() { 
    return this._head; 
  }

  toString() { 
    if (this._head == null) {
      return ''; 
    } 

    let objString = `${this._head._element}`;
    let current = this._head._next;

    for (let i = 1; i < this.size() && current != null; i++) {
      objString = `${objString},${current._element}`; 
      current = current._next; 
    } 

    return objString;
  }
}

// 测试
const list = new LinkedList(); 
console.log(list.isEmpty());
list.push(15); 
list.push(10);
list.push(14); 
list.push(11);
console.log(list.indexOf(14));
list.removeAt(list.indexOf(14))
list.remove(11);
list.insert(55,0);
list.insert(56,1);
console.log(list.size());
console.log(list.getHead());
console.log(list.isEmpty());
console.log(list.toString());